using System;
using System.Threading.Tasks;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using Windows.Storage.Streams;
using Xiaowei.Models;
using Log = Xiaowei.Diagnostics.Debug;
using Debug = Xiaowei.Diagnostics.Debug;
using Network.Http;
using Xiaowei.Settings;
using System.Threading;
using HpApi.Types;
using HpApi;
using HP.UtilsLib;
using System.Collections;
using System.Collections.Generic;

namespace Xiaowei.Helpers
{
    public class ServerApi
    {
        public enum SupportedApp
        {
            QQMusic = 1001,
            TecentVideo = 1002
        }

        public enum AppError
        {
            RequestFailed = -1,
            NetworkError = 404,
            HPSeviceInvalid = 600,
            SSIDInvalid = 601,
            SerialNumberInvalid = 602,
            SerialNumberNotFound = 603,
            UserCredentialInvalid = 604
        }

        public delegate void ErrorHandler(AppError error, string message);
        public event ErrorHandler ErrorOccured;
        public Signature Signature { get; set; }
        public static string SN { get; set; }
        public static string SSID { get; set; }
        public static PlatformWakeupType WakeupType { get; private set; }
        public IList<SkillInfo> Skills { get; set; }
        public static string Telphone { get; set; }
        public static string QrCodeUrl { get; set; }
        public static string MobileTelphone { get; set; }
        static Token token = null;
        public static Token Token {
            get
            {
                if(token == null || string.IsNullOrEmpty(token.Value) || token.ExpireIn <= DateTimeHelper.ToUnix(DateTime.Now))
                {
                    Debug.WriteLine("Need to update token.");
                    token = Instance.InitAsync(SN, SSID).GetAwaiter().GetResult();
                    Debug.WriteLine("update token finised.");
                }
                return token;
            }

            set
            {
                token = value;
            } 
        }
        private string Host => LocalSettings.HostAddress;
        private string ApiVersion => "/xiaowei/client/v1";

        public async Task<Token> InitAsync(string sn, string ssid)
        {
            if (string.IsNullOrEmpty(sn) || string.IsNullOrEmpty(ssid)) return null;

            Log.WriteLine($"ServerApi - InitAsync, sn:{sn}, ssid:{ssid}");

            //HpApi.HpApi.HpApIResponse += Models.SaleHelpModel.GetQrCodeUrl;
             var result = await HpApi.HpApi.GetSignature(Host + ApiVersion, sn, ssid).ConfigureAwait(false);
            if (result.Error.Code == 201)
            {
                Signature = result.Sign;
                Skills = result.SkillInfos;
                WakeupType = result.WakeupType;
                Telphone = result.Telphone;
                MobileTelphone = result.MobileTelphone;
                QrCodeUrl = result.QrCodeUrl;
                return new Token()
                {
                    Value = result.Token.Value,
                    Type = result.Token.Type,
                    ExpireIn = DateTimeHelper.ToUnix(DateTime.Now) + result.Token.ExpireIn
                };
            }
            else
            {
                ErrorOccured?.Invoke((AppError)result.Error.Code, result.Error.Message);
                return null;
            }
        }

        public async Task<string> GetDriverList(string pn,string driverName)
        {
            Log.WriteLine($"ServerApi - GetDriverList");

            return await HpApi.HpApi.GetDriverList(Host + ApiVersion, Token.Value, Token.Type, pn,driverName).ConfigureAwait(false);
        }

        public async Task<string> GetMaintainPosition(double latitude,double longitude)
        {
            Log.WriteLine($"ServerApi - GetMaintainPosition");

            return await HpApi.HpApi.GetMaintainPosition(Host + ApiVersion, Token.Value, Token.Type, latitude, longitude).ConfigureAwait(false);
        }

        public async Task<VersionAck> CheckVersion(string version)
        {
            Log.WriteLine($"ServerApi - CheckVersion");

            return await HpApi.HpApi.CheckVersion(Host + ApiVersion, Token.Value, Token.Type, version).ConfigureAwait(false);
        }

        public async Task<Dictionary<string,string>> GetAPPs()
        {
            Log.WriteLine($"ServerApi - CheckVersion");

            return await HpApi.HpApi.GetAPPs(Host + ApiVersion, Token.Value, Token.Type).ConfigureAwait(false);
        }

        public async Task<bool> DownloadRecord(int appId, string customerId, string deviceName)
        {
            Log.WriteLine($"ServerApi - DownloadRecord, appId:{appId}, customerId:{customerId}, deviceName:{deviceName}");
            return await HpApi.HpApi.DownloadRecord(appId, customerId, deviceName, Host + ApiVersion, Token.Value, Token.Type).ConfigureAwait(false);
        }

        public async Task<bool> LoginRecord(string customerId, string deviceName)
        {
            customerId = HashString(HashAlgorithmNames.Md5, customerId);

            Log.WriteLine($"ServerApi - LoginRecord, customerId:{customerId}, deviceName:{deviceName}");
            return await HpApi.HpApi.LoginRecord(customerId, deviceName, Host + ApiVersion, Token.Value, Token.Type).ConfigureAwait(false);
        }

        public async Task<bool> Feedback(string customerId, string serialNumber, string appVersion, string email, string comments)
        {
            customerId = HashString(HashAlgorithmNames.Md5, customerId);

            Log.WriteLine($"ServerApi - Feedback, customerId:{customerId}, serial_number:{serialNumber},app_version:{appVersion}, email:{email},comments:{comments}");
            return await HpApi.HpApi.Feedback(customerId, serialNumber, appVersion, email, comments, Host + ApiVersion, Token.Value, Token.Type).ConfigureAwait(false);
        }
        public async Task<string> GetObligationMessage(string pn, string sn)
        {
            Log.WriteLine($"ServerApi - GetObligationMessage");

            return await HpApi.HpApi.GetObligationMessage(Host + ApiVersion, Token.Value, Token.Type, pn, sn).ConfigureAwait(false);
        }
        public async Task<bool> DauRecord(string customerId, string serialNumber)
        {
            customerId = HashString(HashAlgorithmNames.Md5, customerId);

            Log.WriteLine($"ServerApi - DauRecord, customerId:{customerId}, serialNumber:{serialNumber}");
            return await HpApi.HpApi.DauRecord(customerId, serialNumber, Host + ApiVersion, Token.Value, Token.Type).ConfigureAwait(false);
        }

        public async Task<AsrInfo>GetAsrPidKeyAsync()
        {
            return await HpApi.HpApi.GetAsrPidAndKeyAsync(Host + ApiVersion).ConfigureAwait(false);
        }

        private ServerApi()
        {
            HttpRequest.NetworkDisconnected += () =>
            {
                ErrorOccured?.Invoke(AppError.NetworkError, "Network has problem.");
            };
        }

        public static ServerApi Instance => LazyInstance.Value;

        private static readonly Lazy<ServerApi> LazyInstance = new Lazy<ServerApi>(() =>
        {
            return new ServerApi();
        }, LazyThreadSafetyMode.ExecutionAndPublication);

        private string HashString(string strAlgName, string strMsg)
        {
            // Convert the message string to binary data.
            IBuffer buffUtf8Msg = CryptographicBuffer.ConvertStringToBinary(strMsg, BinaryStringEncoding.Utf8);

            // Create a HashAlgorithmProvider object.
            HashAlgorithmProvider objAlgProv = HashAlgorithmProvider.OpenAlgorithm(strAlgName);

            // Demonstrate how to retrieve the name of the hashing algorithm.
            string strAlgNameUsed = objAlgProv.AlgorithmName;

            // Hash the message.
            IBuffer buffHash = objAlgProv.HashData(buffUtf8Msg);

            // Verify that the hash length equals the length specified for the algorithm.
            if (buffHash.Length != objAlgProv.HashLength)
            {
                throw new Exception("There was an error creating the hash");
            }

            // Convert the hash to a string (for display).
            string strHashBase64 = CryptographicBuffer.EncodeToBase64String(buffHash);

            // Return the encoded string
            return strHashBase64;
        }
    }
}
